package com.ideaaedi.commonspring.lite.ratelimiter;

import org.springframework.expression.BeanResolver;
import org.springframework.expression.common.TemplateParserContext;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * 限流注解
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.10.17.LTS17
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
    
    /**
     * 限流器名称唯一标识（支持spel）
     * <pre>
     *  spel表达式（注：因为本方法中用到了{@link TemplateParserContext}， 所以需要使用#{}将原生的spel包起来，形成最终的表达式）
     *  1、获取属性示例：{@code #{#param1.fieldA} }
     *  2、调用方法示例：{@code #{#param2.methodA()} }
     *  3、调用spring-bean示例：{@code #{@userService.getUsername(#userId)}},
     *     注：使用之前需要初始化bean解析器{@link com.ideaaedi.commonspring.parser.SpelUtil#initBeanResolver(BeanResolver)}
     *        初始化bean解析器示例：{@code SpelUtil.initBeanResolver(new BeanFactoryResolver(applicationContext)); }
     *  4、调用静态字段示例：{@code #{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE} }
     *     注：若调用的是java.lang.包下的类，可以不指定包名
     *  5、调用静态方法示例：{@code #{T(java.util.Objects).nonNull(#returnObj)} }
     *     注：若调用的是java.lang.包下的类，可以不指定包名
     *  6、枚举示例1：{@code {T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.name()} }
     *     注：和调用静态方法是一样的
     *  7、枚举示例2：{@code #{T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.key(#user.account, #user.phone)} }
     *     注：和调用静态方法是一样的
     *  8、判断示例1：{@code #{#code == 200} }
     *  9、判断示例2：{@code #{#user == null} }
     *  10、......
     *
     *  更多spel语法见{@link com.ideaaedi.commonspring.parser.SpelUtil}
     * </pre>
     */
    String uid();

    /**
     * 滑动时间窗内的最大令牌数
     * <pre>
     * 注：若小于等于0，则忽略限流
     * </pre>
     */
    long tokenLimit();

    /**
     * 滑动时间窗时长
     * <pre>
     * 注：若小于等于0，则忽略限流
     * </pre>
     */
    long interval();

    /**
     * 滑动时间窗时长单位
     *
     * <pre>
     * 当前仅支持
     * {@link TimeUnit#MILLISECONDS}
     * {@link TimeUnit#SECONDS}
     * {@link TimeUnit#MINUTES}
     * {@link TimeUnit#HOURS}
     * {@link TimeUnit#DAYS}
     * 配置其余值将跳过限流校验
     * <pre/>
     */
    TimeUnit intervalUnit() default TimeUnit.SECONDS;

    /**
     * 执行限流校验的条件。当为空或结果为true时执行限流校验；否则不执行限流校验。（支持spel）
     * <pre>
     *  spel表达式（注：因为本方法中用到了{@link TemplateParserContext}， 所以需要使用#{}将原生的spel包起来，形成最终的表达式）
     *  1、获取属性示例：{@code #{#param1.fieldA} }
     *  2、调用方法示例：{@code #{#param2.methodA()} }
     *  3、调用spring-bean示例：{@code #{@userService.getUsername(#userId)}},
     *     注：使用之前需要初始化bean解析器{@link com.ideaaedi.commonspring.parser.SpelUtil#initBeanResolver(BeanResolver)}
     *        初始化bean解析器示例：{@code SpelUtil.initBeanResolver(new BeanFactoryResolver(applicationContext)); }
     *  4、调用静态字段示例：{@code #{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE} }
     *     注：若调用的是java.lang.包下的类，可以不指定包名
     *  5、调用静态方法示例：{@code #{T(java.util.Objects).nonNull(#returnObj)} }
     *     注：若调用的是java.lang.包下的类，可以不指定包名
     *  6、枚举示例1：{@code {T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.name()} }
     *     注：和调用静态方法是一样的
     *  7、枚举示例2：{@code #{T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.key(#user.account, #user.phone)} }
     *     注：和调用静态方法是一样的
     *  8、判断示例1：{@code #{#code == 200} }
     *  9、判断示例2：{@code #{#user == null} }
     *  10、......
     *
     *  更多spel语法见{@link com.ideaaedi.commonspring.parser.SpelUtil}
     * </pre>
     */
    String condition() default "";

    /**
     * 获取令牌失败时，抛出的异常中携带的附加信息
     */
    String additionalInfo() default "";
}
